/**
 * 
 */
package org.swing.utility.jai.canvas;

import java.awt.Dimension;
import java.awt.Event;
import java.awt.Graphics;
import java.awt.Image;

/**
 * @author lqnhu
 *
 */
public class ImageCanvas3D extends Canvas3D {
	static int timeout = 1000; /* Image Tracking Interval */
	Image img; /* Current Image */
	double scale = 1.0; /* Current Image Scale */
	int offsetX, offsetY; /* Image Offset Pair */
	int mouseX, mouseY; /* Last Mouse Down Position */
	int imageWidth, imageHeight; /* Image Width and Height */
	int pszx = 12; /* Preferred Size X */
	int pszy = 12; /* Preferred Size Y */
	int xScale = 1; /* Extra X Scale */
	int yScale = 1; /* Extra Y Scale */
	boolean mousePressed = false;/* If Mouse Pressed */
	boolean needTrack = false; /* If Need Image Tracking */

	/** Constructor */
	public ImageCanvas3D() {
		setBackground(black);
	}

	/* Setting Parameters */
	public Image getImage() {
		return img;
	}

	public double getScale() {
		return scale;
	}

	public int getOffsetX() {
		return offsetX;
	}

	public int getOffsetY() {
		return offsetY;
	}

	public int getImageWidth() {
		return imageWidth;
	}

	public int getImageHeight() {
		return imageHeight;
	}

	public int getScaledWidth() {
		return (int) (scale * xScale * imageWidth + 0.5);
	}

	public int getScaledHeight() {
		return (int) (scale * yScale * imageHeight + 0.5);
	}

	public void setScale(int _xScale, int _yScale) {
		xScale = _xScale;
		yScale = _yScale;
	}

	/* Preferred Size Setting */
	public void setPreferredSize(int _x, int _y) {
		pszx = _x;
		pszy = _y;
	}

	public Dimension preferredSize() {
		return new Dimension(pszx, pszy);
	}

	/** Drawing Action Here */
	public void update(Graphics g) {
		int w = size().width - d2;
		int h = size().height - d2;
		super.update(g);
		g.setColor(getBackground());
		if (img != null) {
			imageWidth = img.getWidth(this);
			imageHeight = img.getHeight(this);
			int bx = depth - offsetX;
			int by = depth - offsetY;
			int iw = getScaledWidth();
			int ih = getScaledHeight();
			g.drawImage(img, bx, by, iw, ih, this);
			if (offsetX < 0)
				g.fillRect(depth, depth, -offsetX, h);
			if (offsetY < 0)
				g.fillRect(depth, depth, w, -offsetY);
			if (iw - offsetX < w)
				g.fillRect(iw + bx, depth, w - iw + offsetX, ih);
			if (ih - offsetY < h)
				g.fillRect(depth, ih + by, w, h - ih + offsetY);
		} else
			g.fillRect(depth, depth, w, h);
	}

	/** Change Image Scale */
	public void setScale(double _scale, boolean frepaint) {
		if (scale == _scale)
			return;
		scale = _scale;
		if (img != null) {
			Notify(null, img);
			if (frepaint)
				repaint();
		}
	}

	/* Change Image Offset */
	public synchronized void setOffset(int x, int y, boolean frepaint) {
		if ((x == offsetX) && (y == offsetY))
			return;
		offsetX = x;
		offsetY = y;
		if (frepaint)
			repaint();
	}

	/** Image Update Tracking */
	public boolean imageUpdate(Image img, int infoFlags, int x, int y,
			int width, int height) {
		boolean ret = true;
		if (img != this.img)
			return false;
		if ((infoFlags & WIDTH) != 0)
			imageWidth = width;
		if ((infoFlags & HEIGHT) != 0)
			imageHeight = height;
		if (((infoFlags & WIDTH) != 0) && ((infoFlags & HEIGHT) != 0))
			Notify(null, img);
		if ((infoFlags & (FRAMEBITS | ALLBITS)) != 0) {
			ret = false;
			imageComplete();
		} else if ((infoFlags & SOMEBITS) != 0) {
			if (!needTrack && (img == this.img)) {
				needTrack = true;
				new Delay(timeout, -1, this);
			}
		}
		if ((infoFlags & ERROR) != 0)
			ret = false;
		return ret;
	}

	/** Change Current Image */
	public void updateImage(Image _img) {
		img = _img;
		if ((img != null) && !prepareImage(img, this)) {
			if (!needTrack) {
				needTrack = true;
				repaint();
				new Delay(timeout, -1, this);
			}
		} else
			imageComplete();
	}

	/** Called when Image is Prepared Complete */
	public void imageComplete() {
		needTrack = false;
		repaint();
		Notify(null, img);
	}

	/** Respong to Delay Event Action */
	public boolean keyDown(Event ev, int key) {
		if (key < 0) {
			if (needTrack) {
				if (!prepareImage(img, this)) {
					repaint();
					new Delay(timeout, key, this);
				} else
					imageComplete();
			}
		}
		return true;
	}

	/** X Delta relative to Last Mouse Down Position */
	public int mouseDeltaX(int x) {
		return mouseX - x;
	}

	/** Y Delta relative to Last Mouse Down Position */
	public int mouseDeltaY(int y) {
		return mouseY - y;
	}

	/** Mouse Down Event */
	public boolean mouseDown(Event ev, int x, int y) {
		mousePressed = true;
		mouseX = x + offsetX;
		mouseY = y + offsetY;
		Notify(ev, this);
		return true;
	}

	/** Delay Mouse Drag Event */
	public boolean delayDrag(Event ev, int x, int y) {
		if (mousePressed)
			Notify(ev, this);
		return true;
	}

	/** Mouse Up Event */
	public boolean mouseUp(Event ev, int x, int y) {
		mousePressed = false;
		return true;
	}
}
